package com.xyazm.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyazm.common.constants.RedisLockKeyConstants;
import com.xyazm.common.enums.*;
import com.xyazm.common.exception.BaseBizException;
import com.xyazm.common.message.ActualRefundMessage;
import com.xyazm.common.redis.RedisLock;
import com.xyazm.common.utils.ExtJsonUtil;
import com.xyazm.common.utils.ParamCheckUtil;
import com.xyazm.order.domain.dto.*;
import com.xyazm.order.mq.config.MQMessage;
import com.xyazm.order.mq.producer.LackItemProducer;
import com.xyazm.order.remote.ProductRemote;
import com.xyazm.order.dao.*;
import com.xyazm.order.domain.entity.*;
import com.xyazm.order.domain.param.LackParam;
import com.xyazm.order.enums.*;
import com.xyazm.order.exception.OrderBizException;
import com.xyazm.order.exception.OrderErrorCodeEnum;
import com.xyazm.order.manager.AfterSaleAmountManager;
import com.xyazm.order.manager.impl.OrderNoManagerImpl;
import com.xyazm.order.mq.producer.DefaultProducer;
import com.xyazm.order.service.IOrderLackService;
import com.xyazm.product.domain.dto.ProductSkuDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
public class OrderLackServiceImpl implements IOrderLackService {

    @Autowired
    private ProductRemote productRemote;
    @Autowired
    private OrderInfoDAO orderInfoDAO;
    @Autowired
    private OrderItemDAO orderItemDAO;
    @Autowired
    private AfterSaleInfoDAO afterSaleInfoDAO;
    @Autowired
    private AfterSaleItemDAO afterSaleItemDAO;
    @Autowired
    private AfterSaleRefundDAO afterSaleRefundDAO;
    @Autowired
    private OrderNoManagerImpl orderNoManager;
    @Autowired
    private AfterSaleAmountManager afterSaleAmountManager;
    @Autowired
    private DefaultProducer defaultProducer;
    @Autowired
    private RedisLock redisLock;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private LackItemProducer lackItemProducer;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public LackDTO lockItem(LackParam param) {
        // 校验缺品取消订单参数
        checkLackParam(param);
        // 加锁防并发
        String orderId = param.getOrderId();
        String lockKey = RedisLockKeyConstants.LACK_REQUEST_KEY + orderId;
        try {
            if (!redisLock.tryLock(lockKey)) {
                throw new OrderBizException(OrderErrorCodeEnum.ORDER_NOT_ALLOW_TO_LACK);
            }
            // 查询订单
            OrderInfoDO orderInfoDO = orderInfoDAO.getByOrderId(orderId);
            ParamCheckUtil.checkObjectNonNull(orderInfoDO, OrderErrorCodeEnum.ORDER_NOT_FOUND);
            // 校验订单是否可以发起缺品
            // 可以发起缺品的前置条件：
            // 1.订单的状态为："已出库"
            // 2.订单未发起过缺品
            // 解释一下为什么是"已库存"状态才能发起缺品：
            // 缺品的业务逻辑是这样的，当订单支付后，进入履约流程，仓库人员捡货当时候发现现有商品无法满足下单所需，即"缺品"了
            // 仓库人员首先会将通知订单系统将订单的状态变为"已出库"，然后会再来调用这个缺品的接口
            if (!OrderStatusEnum.canLack().contains(orderInfoDO.getOrderStatus()) || isOrderLacked(orderInfoDO)) {
                throw new OrderBizException(OrderErrorCodeEnum.ORDER_NOT_ALLOW_TO_LACK);
            }
            // 查询订单item
            List<OrderItemDO> orderItemDOList = orderItemDAO.listByOrderId(orderId);
            // 校验具体的缺品项
            Set<LackParam.LackItemParam> lackItemList = param.getLackItemList();
            List<LackItemDTO> lackItemDTOList = new ArrayList<>();
            for (LackParam.LackItemParam lackItem : lackItemList) {
                lackItemDTOList.add(checkLackItem(orderInfoDO, orderItemDOList, lackItem));
            }
            // 执行缺品处理逻辑
            return executeLackRequest(param, orderInfoDO,lackItemDTOList);
        } catch (Exception e) {
            throw new OrderBizException(e.getMessage());
        }finally {
            redisLock.unlock(lockKey);
        }
    }

    @Override
    public boolean isOrderLacked(OrderInfoDO orderInfoDO) {
        OrderExtJsonDTO orderExtJson = ExtJsonUtil.parseExtJson(orderInfoDO.getExtJson(),OrderExtJsonDTO.class);
        if(null != orderExtJson) {
            return orderExtJson.getLackFlag();
        }
        return false;
    }

    /**
     * 执行缺品处理逻辑
     * @author xuyou
     * @date: 2022/7/6 15:41
     */
    private LackDTO executeLackRequest(LackParam param, OrderInfoDO orderInfoDO, List<LackItemDTO> lackItems) throws MQClientException {
        // 生成缺品售后单
        AfterSaleInfoDO lackAfterSaleInfoDO = buildLackAfterSaleInfo(orderInfoDO);
        // 生成缺品售后单item
        List<AfterSaleItemDO> afterSaleItems = new ArrayList<>();
        lackItems.forEach(item -> {
            afterSaleItems.add(buildLackAfterSaleItem(orderInfoDO, lackAfterSaleInfoDO, item));
        });
        // 计算订单缺品退款总金额
        Integer lackApplyRefundAmount = afterSaleAmountManager.calculateOrderLackApplyRefundAmount(afterSaleItems);
        Integer lackRealRefundAmount = afterSaleAmountManager.calculateOrderLackRealRefundAmount(afterSaleItems);
        lackAfterSaleInfoDO.setApplyRefundAmount(lackApplyRefundAmount);
        lackAfterSaleInfoDO.setRealRefundAmount(lackRealRefundAmount);

        // 构造售后退款单
        AfterSaleRefundDO afterSaleRefundDO = buildLackAfterSaleRefund(orderInfoDO, lackAfterSaleInfoDO);
        // 构造订单缺品扩展信息
        OrderExtJsonDTO lackExtJson = buildOrderLackExtJson(param, lackAfterSaleInfoDO);

        // 构造订单缺品信息
        OrderLackInfoMessageDTO orderLackInfoMessageDTO = OrderLackInfoMessageDTO.builder()
                .afterSaleInfoDO(lackAfterSaleInfoDO)
                .afterSaleItems(afterSaleItems)
                .afterSaleRefundDO(afterSaleRefundDO)
                .orderExtJsonDTO(lackExtJson)
                .orderId(orderInfoDO.getOrderId())
                .build();

        // 获取缺品处理的生产者组件
        TransactionMQProducer producer = lackItemProducer.getProducer();
        setLackItemTransactionListener(producer);
        // 发送缺品退款的消息
        sendLackItemMessage(orderInfoDO.getOrderId(), lackAfterSaleInfoDO.getAfterSaleId(),
                orderLackInfoMessageDTO,producer);
        return new LackDTO(orderInfoDO.getOrderId(), lackAfterSaleInfoDO.getAfterSaleId());
    }

    private void setLackItemTransactionListener(TransactionMQProducer producer) {
        producer.setTransactionListener(new TransactionListener() {
            @Override
            public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                try {
                    OrderLackInfoMessageDTO orderLackInfo = (OrderLackInfoMessageDTO) arg;
                    //保存缺品数据
                    saveOrderLackInfo(orderLackInfo);
                    return LocalTransactionState.COMMIT_MESSAGE;
                } catch (BaseBizException e) {
                    throw e;
                } catch (Exception e) {
                    log.error("system error", e);
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                }
            }

            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                // 检查缺品售后单是否已经创建
                String body = new String(msg.getBody(), StandardCharsets.UTF_8);
                ActualRefundMessage actualRefundMessage = JSON.parseObject(body, ActualRefundMessage.class);
                String afterSaleId = actualRefundMessage.getAfterSaleId();
                AfterSaleInfoDO afterSaleInfoDO = afterSaleInfoDAO.getOneByAfterSaleId(afterSaleId);
                if (afterSaleInfoDO != null) {
                    return LocalTransactionState.COMMIT_MESSAGE;
                }
                return LocalTransactionState.ROLLBACK_MESSAGE;
            }
        });



    }

    /**
     * 保存缺品数据
     * @param orderLackInfo
     */
    public void saveOrderLackInfo(OrderLackInfoMessageDTO orderLackInfo) {
        transactionTemplate.execute(action -> {
        // 1、存储售后单,item和退款单;
        afterSaleInfoDAO.save(orderLackInfo.getAfterSaleInfoDO());
        afterSaleItemDAO.saveBatch(orderLackInfo.getAfterSaleItems());
        afterSaleRefundDAO.save(orderLackInfo.getAfterSaleRefundDO());
        // 2、更新订单扩展信息
        orderInfoDAO.updateOrderExtJson(orderLackInfo.getOrderId(), JSONObject.toJSONString(orderLackInfo.getOrderExtJsonDTO()));
            return null;
        });
    }


    /**
     * 发送缺品退款的MQ
     * @author xuyou
     * @date: 2022/7/6 16:06
     */
    private void sendLackItemMessage(String orderId, String afterSaleId,OrderLackInfoMessageDTO orderLackInfo
            ,TransactionMQProducer producer) throws MQClientException {
        ActualRefundMessage actualRefundMessage = new ActualRefundMessage();
        actualRefundMessage.setOrderId(orderId);
        actualRefundMessage.setAfterSaleId(afterSaleId);
        byte[] body = JSON.toJSONString(actualRefundMessage).getBytes(StandardCharsets.UTF_8);
        Message mq = new MQMessage(RocketMqTopicEnum.ACTUAL_REFUND.getTopic(), null, orderId, body);
        producer.sendMessageInTransaction(mq, orderLackInfo);
    }


    /**
     * 构造订单缺品扩展信息
     * @author xuyou
     * @date: 2022/7/6 16:01
     */
    private OrderExtJsonDTO buildOrderLackExtJson(LackParam param, AfterSaleInfoDO afterSaleInfoDO) {
        OrderExtJsonDTO orderExtJson = new OrderExtJsonDTO();
        orderExtJson.setLackFlag(true);
        OrderLackInfoDTO lackInfo = new OrderLackInfoDTO();
        lackInfo.setLackItemList(param.getLackItemList());
        lackInfo.setOrderId(afterSaleInfoDO.getOrderId());
        lackInfo.setApplyRefundAmount(afterSaleInfoDO.getApplyRefundAmount());
        lackInfo.setRealRefundAmount(afterSaleInfoDO.getRealRefundAmount());
        orderExtJson.setLackInfo(lackInfo);
        return orderExtJson;
    }

    /**
     * 构造售后退款单
     * @author xuyou
     * @date: 2022/7/6 15:57
     */
    private AfterSaleRefundDO buildLackAfterSaleRefund(OrderInfoDO orderInfoDO, AfterSaleInfoDO afterSaleInfoDO) {
        //构造售后单
        AfterSaleRefundDO AfterSaleRefundDO = new AfterSaleRefundDO();
        AfterSaleRefundDO.setOrderId(orderInfoDO.getOrderId());
        AfterSaleRefundDO.setPayType(orderInfoDO.getPayType());
        AfterSaleRefundDO.setAfterSaleId(afterSaleInfoDO.getAfterSaleId());
        AfterSaleRefundDO.setRefundAmount(afterSaleInfoDO.getRealRefundAmount());
        AfterSaleRefundDO.setAccountType(AccountTypeEnum.THIRD.getCode());
        AfterSaleRefundDO.setRefundStatus(RefundStatusEnum.UN_REFUND.getCode());
        return AfterSaleRefundDO;
    }

    /**
     * 生成缺品售后单item
     * @author xuyou
     * @date: 2022/7/6 15:49
     */
    private AfterSaleItemDO buildLackAfterSaleItem(OrderInfoDO orderInfoDO, AfterSaleInfoDO lackAfterSaleOrder, LackItemDTO lackItemDTO) {
        Integer lackNum = lackItemDTO.getLackNum();
        ProductSkuDTO productSku = lackItemDTO.getProductSku();
        OrderItemDO orderItemDO = lackItemDTO.getOrderItemDO();

        AfterSaleItemDO afterSaleItemDO = new AfterSaleItemDO();
        afterSaleItemDO.setAfterSaleId(lackAfterSaleOrder.getAfterSaleId());
        afterSaleItemDO.setOrderId(orderInfoDO.getOrderId());
        afterSaleItemDO.setProductName(productSku.getProductName());
        afterSaleItemDO.setSkuCode(productSku.getSkuCode());
        afterSaleItemDO.setReturnQuantity(lackNum);
        afterSaleItemDO.setProductImg(orderItemDO.getProductImg());
        afterSaleItemDO.setOriginAmount(orderItemDO.getOriginAmount());
        // 计算sku缺品退款金额
        afterSaleItemDO.setApplyRefundAmount(orderItemDO.getSalePrice() * lackNum);
        // 计算订单条目缺品实际退款金额
        Integer realRefundAmount = afterSaleAmountManager.calculateOrderItemLackRealRefundAmount(orderItemDO, lackNum);
        afterSaleItemDO.setRealRefundAmount(realRefundAmount);
        return afterSaleItemDO;
    }

    /**
     * 生成缺品售后单
     * @author xuyou
     * @date: 2022/7/6 15:43
     */
    private AfterSaleInfoDO buildLackAfterSaleInfo(OrderInfoDO orderInfoDO) {
        // 生成售后单号
        String orderId = orderInfoDO.getOrderId();
        String userId = orderInfoDO.getUserId();
        String afterSaleId = orderNoManager.genOrderId(OrderNoTypeEnum.AFTER_SALE.getCode(), userId);
        //构造售后单
        AfterSaleInfoDO afterSaleInfoDO = new AfterSaleInfoDO();
        afterSaleInfoDO.setAfterSaleId(afterSaleId);
        afterSaleInfoDO.setOrderId(orderId);
        afterSaleInfoDO.setOrderSourceChannel(BusinessIdentifierEnum.SELF_MALL.getCode());
        afterSaleInfoDO.setUserId(userId);
        afterSaleInfoDO.setOrderType(OrderTypeEnum.NORMAL.getCode());
        afterSaleInfoDO.setAfterSaleType(AfterSaleTypeEnum.RETURN_MONEY.getCode());
        afterSaleInfoDO.setAfterSaleTypeDetail(AfterSaleTypeDetailEnum.LACK_REFUND.getCode());
        afterSaleInfoDO.setApplySource(AfterSaleApplySourceEnum.SYSTEM.getCode());
        afterSaleInfoDO.setAfterSaleStatus(AfterSaleStatusEnum.REVIEW_ACCEPT.getCode());
        afterSaleInfoDO.setApplyTime(new Date());
        afterSaleInfoDO.setReviewTime(new Date());
        return afterSaleInfoDO;
    }


    /**
     * 校验缺品项
     * @author xuyou
     * @date: 2022/7/6 15:30
     */
    private LackItemDTO checkLackItem(OrderInfoDO orderInfoDO, List<OrderItemDO> orderItemDOList, LackParam.LackItemParam lackItem) {
        String lockSkuCode = lackItem.getSkuCode();
        Integer lackNum = lackItem.getLackNum();
        // 参数校验
        ParamCheckUtil.checkStringNonEmpty(lockSkuCode, OrderErrorCodeEnum.SKU_CODE_IS_NULL);
        ParamCheckUtil.checkIntMin(lackNum, 1, OrderErrorCodeEnum.LACK_NUM_IS_LT_0);

        // 根据skuCode查询商品详情
        ProductSkuDTO productSkuDTO = productRemote.getProductSku(lockSkuCode, orderInfoDO.getSellerId());
        ParamCheckUtil.checkObjectNonNull(productSkuDTO, OrderErrorCodeEnum.PRODUCT_SKU_CODE_ERROR, lockSkuCode);

        // 找到item中对应的缺品sku item
        OrderItemDO orderItemDO = orderItemDOList.stream()
                .filter(item -> item.getSkuCode().equals(lockSkuCode))
                .findFirst().orElse(null);
        ParamCheckUtil.checkObjectNonNull(orderItemDO, OrderErrorCodeEnum.LACK_ITEM_NOT_IN_ORDER, lockSkuCode);

        // 缺品商品数量不能 >= 下单商品数量
        if (lackItem.getLackNum() >= orderItemDO.getSaleQuantity()) {
            throw new OrderBizException(OrderErrorCodeEnum.LACK_NUM_IS_GE_SKU_ORDER_ITEM_SIZE);
        }
        // 构造返参
        return new LackItemDTO(orderItemDO, lackNum, productSkuDTO);
    }

    /**
     * 校验缺品取消订单参数
     * @author xuyou
     * @date: 2022/7/6 15:23
     */
    private void checkLackParam(LackParam param) {
        ParamCheckUtil.checkStringNonEmpty(param.getOrderId(), OrderErrorCodeEnum.ORDER_ID_IS_NULL);
        ParamCheckUtil.checkCollectionNonEmpty(param.getLackItemList(), OrderErrorCodeEnum.LACK_ITEM_IS_NULL);
    }
}
